package vip.ifmm.lehouse.commodity.service.api.impl;

import com.alibaba.dubbo.config.annotation.Service;
import com.github.pagehelper.PageInfo;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
import vip.ifmm.lehouse.commodity.service.api.CommodityCommentService;
import vip.ifmm.lehouse.commodity.service.dao.CommodityCommentDao;
import vip.ifmm.lehouse.commodity.service.event.StarToSqlEvent;
import vip.ifmm.lehouse.commodity.service.utils.PageUtil;
import vip.ifmm.lehouse.commodity.service.utils.RedisUtil;
import vip.ifmm.lehouse.common.bo.SessionBo;
import vip.ifmm.lehouse.common.dto.ServerResponse;
import vip.ifmm.lehouse.common.enums.status.CommentStatus;
import vip.ifmm.lehouse.common.model.Comment;
import vip.ifmm.lehouse.common.model.User;
import vip.ifmm.lehouse.common.util.CodeUtil;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.Semaphore;

/**
 * 评论业务实现类
 *
 * @author Red
 * email: 1318944013@qq.com
 * date: 2019/6/21 14:41
 */
@Service(owner = "red",
        mock = "vip.ifmm.lehouse.commodity.service.api.mock.CommodityCommentServiceMock")
public class CommodityCommentServiceImpl implements CommodityCommentService {

    // 评论dao
    @Resource
    private CommodityCommentDao commentDao;
    // 允许并行插入个数
    @Value("${parallel-insert-num}")
    private int parallelInsertNum = 0;
    // 应用上下文
    @Resource
    ApplicationContext context;

    // 信号量，用来控制并行插入个数
    private Semaphore semaphore;

    // Redis中评论点赞数前缀
    @Value("${redis-star-key}")
    private String REDIS_STAR_KEY;

    @Value("${commodity-key}")
    private String REDIS_COMMODITY_KEY;

    @PostConstruct
    public void init() {
        // 初始化信号量个数
        semaphore = new Semaphore(parallelInsertNum);
    }

    // 发表评论
    @Override
    public ServerResponse addComment(Comment comment, SessionBo sessionBo) {
        // 检查session
        if (sessionBo == null || sessionBo.getUser() == null) {
            return ServerResponse.response(CommentStatus.SESSION_NOT_EXISTED);
        }
        User user = sessionBo.getUser();
        // 设置评论属性
        setComment(comment, user);
        // 检查评论的合法性
        ServerResponse checkResult = checkComment(comment, user);
        if (!checkResult.isSuccess()) {
            return checkResult;
        }
        // 插入评论，获取结果
        int result = insertComment(comment);
        // 插入失败
        if (result == 0) {
            return ServerResponse.response(CommentStatus.COMMENT_FAILURE);
        }

        // 如果插入成功
        // 初始化评论的点赞数
        RedisUtil.set(REDIS_STAR_KEY + comment.getCommentId(), 0, -1);

        // 发布点赞数写回数据库事件
        context.publishEvent(new StarToSqlEvent(comment));

        // 评论成功
        return ServerResponse.response(CommentStatus.COMMENT_SUCCESS);
    }

    // 删除评论
    @Override
    public ServerResponse deleteComment(Integer commodityId, Integer commentId) {
        Comment comment = commentDao.getCommentById(commentId);
        // 评论不存在
        if (comment == null) {
            return ServerResponse.response(CommentStatus.COMMENT_IS_NOT_EXISTED);
        }
        int result = commentDao.deleteCommentById(commentId);
        // 删除失败
        if (result == 0) {
            return ServerResponse.response(CommentStatus.COMMENT_DELETE_FAILURE);
        }
        // 删除成功
        // 删除Redis中该评论的点赞数
        RedisUtil.delete(REDIS_STAR_KEY + commentId);
        // Redis中重新缓存评论列表
        RedisUtil.set(REDIS_COMMODITY_KEY + commodityId,
                PageUtil.pageInfo(commentDao.listComments(commodityId)), 900);
        // 删除成功
        return ServerResponse.response(CommentStatus.COMMENT_DELETE_SUCCESS);
    }

    // 查询指定商品的评论列表
    @Override
    public ServerResponse listComment(Integer commodityId,
                                      Integer pageNum, Integer pageSize) {
        // 从Redis中查找
        PageInfo pageInfo = RedisUtil.get(REDIS_COMMODITY_KEY + commodityId, PageInfo.class);
        List<Comment> list;
        // 如果Redis中没有，去查数据库
        if (pageInfo == null) {
            // 分页
            PageUtil.toPage(pageNum);
            // 查询数据库
            list = commentDao.listComments(commodityId);
            pageInfo = PageUtil.pageInfo(list);
            // 设置到Redis中，过期时间15分钟
            RedisUtil.set(REDIS_COMMODITY_KEY + commodityId, pageInfo, 900);
        }
        return ServerResponse.response(CommentStatus.COMMNET_LIST_SUCCESS,
                pageInfo);
    }


    // 更新Redis中的点赞数
    @Override
    public ServerResponse updateStar(Integer commentId) {
        long result = RedisUtil.incr(REDIS_STAR_KEY + commentId, 1);
        // Redis中自增失败
        if (result <= 0) {
            return ServerResponse.response(CommentStatus.STAR_FAILURE);
        }
        return ServerResponse.response(CommentStatus.STAR_SUCCESS);
    }


    // 判断评论的合法性
    private ServerResponse checkComment(Comment comment, User user) {

        // 参数不合法
        if (CodeUtil.isBlank(comment.getCommentUserNickname()) ||
                comment.getCommentUserId() == 0 || comment.getCommentCommodityId() == 0
                || comment.getCommentStar() != 0 || CodeUtil.isBlank(comment.getCommentWord())) {
            return ServerResponse.response(CommentStatus.COMMENT_IS_NOT_RIGHT);
        }
        // 重复评论
        if (commentDao.getCommentByUserIdAndCommodityId
                (user.getUserId(), comment.getCommentCommodityId()) != null) {
            return ServerResponse.response(CommentStatus.REPEAT_COMMENT);
        }

        // 合法评论
        return ServerResponse.response(CommentStatus.COMMENT_IS_OK);
    }

    // 将评论插入数据库, 最多允许配置的个数进行同时插入
    private int insertComment(Comment comment) {
        int result = 0;
        // 排队插入
        try {
            semaphore.acquire();
            result = commentDao.insertComment(comment);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            semaphore.release();
        }

        return result;
    }

    // 设置评论的属性
    private void setComment(Comment comment, User user) {
        // 注入属性
        comment.setCommentCreateTime(new Date());
        comment.setCommentUpdateTime(new Date());
        comment.setCommentUserId(user.getUserId());
        comment.setCommentStar(0);
    }
}
